#include "image_cache.h"

#include <unistd.h>
#include "hw_app_log.h"
#include "spdlog/stopwatch.h"
#include <iostream>
#include <fstream>

namespace gc
{
    void matwrite(const string& filename, const Mat& mat)
    {
        ofstream fs(filename, fstream::binary);

        // Header
        int type = mat.type();
        int channels = mat.channels();
        fs.write((char*)&mat.rows, sizeof(int));    // rows
        fs.write((char*)&mat.cols, sizeof(int));    // cols
        fs.write((char*)&type, sizeof(int));        // type
        fs.write((char*)&channels, sizeof(int));    // channels

        // Data
        if (mat.isContinuous())
        {
            fs.write(mat.ptr<char>(0), (mat.dataend - mat.datastart));
        }
        else
        {
            int rowsz = CV_ELEM_SIZE(type) * mat.cols;
            for (int r = 0; r < mat.rows; ++r)
            {
                fs.write(mat.ptr<char>(r), rowsz);
            }
        }
        fs.close();
    }

    Mat matread(const string& filename)
    {
        ifstream fs(filename, fstream::binary);

        // Header
        int rows, cols, type, channels;
        fs.read((char*)&rows, sizeof(int));         // rows
        fs.read((char*)&cols, sizeof(int));         // cols
        fs.read((char*)&type, sizeof(int));         // type
        fs.read((char*)&channels, sizeof(int));     // channels

        // Data
        Mat mat(rows, cols, type);
        fs.read((char*)mat.data, CV_ELEM_SIZE(type) * rows * cols);
        fs.close();

        return mat;
    }

    GCImageCache::GCImageCache()
    {
        if (gc_app_config.base_config.alg_config.cache_position == GC_CACHE_POSITION::DISK) {
            auto clearFunc = std::bind(&GCImageCache::ClearThrd, this);
            std::thread clearThrDecode(clearFunc);
            m_clear_thread = std::move(clearThrDecode);
        }
    }

    GCImageCache::~GCImageCache(void)
    {
    }

    bool GCImageCache::Put(const char* img_path, cv::Mat& img)
    {
        return Put(img_path, img, true);
    }

    bool GCImageCache::Put(const char* img_path, cv::Mat& img, bool save_img)
    {
        bool ret = true;
        if (gc_app_config.base_config.alg_config.cache_position == GC_CACHE_POSITION::DISK) {

            LOG_DEBUG("size of m_chan_yuv_image_data=%d", m_chan_yuv_image_data.size());

            // if (m_chan_yuv_image_data.size() > gc_app_config.base_config.alg_config.img_cache_size) {
            //     GCImage gc_image;
            //     gc_image << m_chan_yuv_image_data;
            //     Remove(gc_image.img_path);
            // }

            if (save_img && !img.empty()) {
                // system("sync; echo 1 > /proc/sys/vm/drop_caches"); // 只在容器内生效，无法影响宿主机
                ret = cv::imwrite(img_path, img);
                if (!ret) {
                    LOG_ERROR("cache image failed, img_path=%s, code=%d", img_path, ret);
                    return ret;
                }
            }
            GCImage image;
            strcpy(image.img_path, img_path);
            image >> m_chan_yuv_image_data;
            img.release();
        } else {
            IMG_CACHE.Put(std::string(img_path), img);
            img.release();
        }
        return ret;
    }

    void GCImageCache::Get(const char* img_path, cv::Mat &img)
    {
        if (gc_app_config.base_config.alg_config.cache_position == GC_CACHE_POSITION::DISK) {
            img = cv::imread(img_path);
        } else {
            std::pair<caches::fixed_sized_cache<std::string, cv::Mat, caches::FIFOCachePolicy>::const_iterator, bool> cache_data = IMG_CACHE.TryGet(std::string(img_path));
            if (cache_data.second == true) {
                img = cache_data.first->second;
            }         
        }
        if (img.empty()) {
            LOG_ERROR("image not found in cache for img_path=%s, size of IMG_CACHE=%d", img_path, IMG_CACHE.Size());
        }
    }

    bool GCImageCache::Remove(const char* img_path)
    {
        if (gc_app_config.base_config.alg_config.cache_position == GC_CACHE_POSITION::DISK) {
            int disk_ret = std::system(("rm -rf " + std::string(img_path)).c_str());
            return disk_ret == 0;
        } else {
            return IMG_CACHE.Remove(std::string(img_path));
        }
    }

    void GCImageCache::ClearThrd(void)
    {
        LOG_INFO("ClearThrd start");
        while (true) {
            if (m_chan_yuv_image_data.size() < gc_app_config.base_config.alg_config.img_cache_size) {
                usleep(100000);
                continue;
            }
            GCImage gc_image;
            gc_image << m_chan_yuv_image_data;
            std::string cmd("rm -rf ");
            cmd.append("'");
            cmd.append(gc_image.img_path);
            cmd.append("'");
            int ret = system(cmd.c_str());
            if (ret != 0) {
                LOG_ERROR("clear gc image failed, cmd=%s", cmd.c_str());
            }

            // LOG_DEBUG("size of m_chan_yuv_image_data=%d,  gc_app_config.base_config.alg_config.img_cache_size=%d, cmd=%s", m_chan_yuv_image_data.size(), gc_app_config.base_config.alg_config.img_cache_size, cmd.c_str());

        }
    }    
}